/*-------------------------------------------------------------------------
 *
 * supportnodes.h
 *	  规划者支持函数的定义。
 *
 * 此文件定义了“规划者支持函数”的 API，这些
 * 是可以附加到另一个“目标”函数的 SQL 函数（通常用 C 编写），
 * 以便系统获得关于目标函数的额外知识。所有当前的功能都与使用目标
 * 函数的查询规划有关，尽管未来的扩展可能会增加解析器或执行器调用的功能。
 *
 * 支持函数必须具有 SQL 签名
 *		supportfn(internal) returns internal
 * 参数是指向本文件中定义的 Node 类型之一的指针。
 * 结果通常也是 Node 指针，尽管其类型取决于被调用的功能。
 * 在所有情况下，NULL 指针结果（即 PG_RETURN_POINTER(NULL)，而不是 PG_RETURN_NULL()）
 * 表示支持函数无法为给定请求提供有用的功能。
 * 如果支持函数不识别请求的节点类型或无法处理给定情况，则必须返回 NULL 指针，而不是失败；
 * 这允许未来扩展请求情况的集合。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/nodes/supportnodes.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef SUPPORTNODES_H
#define SUPPORTNODES_H

#include "nodes/plannodes.h"

struct PlannerInfo;				/* 避免在此处包含 pathnodes.h */
struct IndexOptInfo;
struct SpecialJoinInfo;
struct WindowClause;

/*
 * Simplify 请求允许支持函数在调用其目标函数时进行计划时简化。例如，不降低其参数允许长度的 varchar 长度强制转换可以被 RelabelType 节点替换，或者 "x + 0" 可以被替换为 "x"。这在规划器的常量折叠过程中被调用，因此可以假设函数的参数已经被简化。
 *
 * 规划器的 PlannerInfo "root" 通常是不需要的，但如果需要获取有关给定节点树中存在的 Var 的信息，可以咨询它。请注意，在某些用法中，root 可能为 NULL。
 *
 * "fcall" 将是调用支持函数目标函数的 FuncExpr。（即使原始解析树节点是操作符调用，这也是正确的；为此合成了一个 FuncExpr。）
 *
 * 结果应为一个语义上等效的转换节点树，或者如果无法进行简化，则返回 NULL。*不要* 返回或修改 *fcall，因为它实际上并不是单独分配的 Node。但可以在结果树中使用 fcall->args 或其部分。
 */
typedef struct SupportRequestSimplify
{
	NodeTag		type;

	struct PlannerInfo *root;	/* 规划器的基础设施 */
	FuncExpr   *fcall;			/* 要被简化的函数调用 */
} SupportRequestSimplify;


/*
 * Selectivity请求允许支持函数为出现在WHERE子句顶层的函数提供选择性估计
 * （因此仅适用于返回布尔值的函数）。
 *
 * 输入参数与提供给运算符限制和连接估算器的参数相同，
 * 只是我们将这两个API统一为一个请求类型。详细信息请参见clause_selectivity()。
 *
 * 如果可以进行估计，将其存储到“selectivity”字段中，并返回SupportRequestSelectivity节点的地址；
 * 估计值必须在0到1之间（包括0和1）。如果无法进行估计，则返回NULL
 * （在这种情况下，规划器将退回到默认估计，传统上为1/3）。
 *
 * 如果目标函数被用作运算符的实现，则不使用支持函数；
 * 而是咨询运算符的限制或连接估算器。
 */
typedef struct SupportRequestSelectivity
{
	NodeTag		type;

	/* 输入字段： */
	struct PlannerInfo *root;	/* 规划器的基础设施 */
	Oid			funcid;			/* 我们询问的函数 */
	List	   *args;			/* 函数的预简化参数 */
	Oid			inputcollid;	/* 函数的输入排序规则 */
	bool		is_join;		/* 这是一种连接还是限制情况？ */
	int			varRelid;		/* 如果限制，目标关系的 RTI */
	JoinType	jointype;		/* 如果连接，外连接类型 */
	struct SpecialJoinInfo *sjinfo; /* 如果外连接，关于连接的信息 */

	/* 输出字段： */
	Selectivity selectivity;	/* 返回的选择性估计 */
} SupportRequestSelectivity;

/*
 * 成本请求允许支持功能为其目标功能提供执行成本估算。成本估算可以包括一次性（查询启动）组件和每次执行组件。
 * 估算不应包括评估目标函数参数的成本，只有目标函数本身。
 *
 * “node”参数通常是调用目标函数的解析节点。在最简单的情况下，这是一个FuncExpr，但它也可能是OpExpr、DistinctExpr、NullIfExpr或WindowFunc，或者将来可能还有其他情况。
 * 如果函数不能假设其参数等同于调用节点所提供的参数，则传递NULL；例如，这发生在聚合支持函数和由RowExpr使用的按列比较运算符中。
 *
 * 如果可以进行估算，将其存储到成本字段中并返回SupportRequestCost节点的地址。如果无法进行估算，则返回NULL，在这种情况下，规划器将依赖于目标函数的procost字段。
 * （注意：虽然procost由cpu_operator_cost自动缩放，但成本请求的输出不是这种情况；支持函数必须自行适当地缩放其结果。）
 */
typedef struct SupportRequestCost
{
	NodeTag		type;

	/* 输入字段： */
	struct PlannerInfo *root;	/* 规划器的基础设施（可能为NULL） */
	Oid			funcid;			/* 我们询问的函数 */
	Node	   *node;			/* 调用函数的解析节点，或NULL */

	/* 输出字段： */
	Cost		startup;		/* 一次性成本 */
	Cost		per_tuple;		/* 每次评估成本 */
} SupportRequestCost;

/*
 * 行请求允许支持功能为其目标函数提供输出行数估算（因此仅适用于返回集合的函数）。
 *
 * “node”参数是调用目标函数的解析节点；目前这始终是FuncExpr或OpExpr。
 *
 * 如果可以进行估算，将其存储到行字段中并返回SupportRequestRows节点的地址。如果无法进行估算，则返回NULL，在这种情况下，规划器将依赖于目标函数的prorows字段。
 */
typedef struct SupportRequestRows
{
	NodeTag		type;

	/* 输入字段： */
	struct PlannerInfo *root;	/* 规划器的基础设施（可能为NULL） */
	Oid			funcid;			/* 我们询问的函数 */
	Node	   *node;			/* 调用函数的解析节点 */

	/* 输出字段： */
	double		rows;			/* 预计返回的行数 */
} SupportRequestRows;

/*
 * IndexCondition请求允许支持功能生成
 * 基于目标函数调用的直接可索引条件，该调用本身
 * 不能索引。目标函数调用必须出现在WHERE或JOIN/ON的
 * 顶层，因此这仅适用于返回布尔值的函数。
 *
 * “节点”参数是调用目标函数的解析节点；
 * 目前这将始终是FuncExpr或OpExpr。调用仅在至少一个函数
 * 参数与索引列的变量或表达式匹配时发生。“indexarg”
 * 标识匹配的参数（它是节点args列表中参数的零基索引）。
 *
 * 如果变换是可能的，返回一个直接可索引的条件
 * 表达式列表，否则返回NULL。（使用列表是因为有时
 * 生成多个可索引条件是有用的，例如，当具有常量前缀的
 * LIKE产生两个条件：>=和<。）
 *
 * “直接可索引”意味着该条件必须能够直接被
 * 索引机制执行。通常这意味着它是一个二元的OpExpr，
 * 左侧是索引列值，右侧是伪常量，且操作符属于
 * 索引列的操作符家族。其他可能性包括RowCompareExpr、
 * ScalarArrayOpExpr和NullTest，具体取决于索引类型；
 * 但这些对于派生索引条件的用处似乎较小。“伪常量”
 * 意味着右侧表达式不能包含任何易变函数，
 * 也不能包含与索引相关表的Vars；使用
 * is_pseudo_constant_for_index()进行检查。
 * （注意：如果传递的“节点”是OpExpr，核心规划器已
 * 验证非索引键操作数是伪常量；但当“节点”是
 * FuncExpr时，它不会检查，因为它不知道哪个
 * 函数的参数您可能需要在索引比较值中使用。）
 *
 * 在许多情况下，可以生成索引条件，但它强度
 * 不及函数条件本身；例如，带有常量前缀的LIKE
 * 可以根据前缀生成索引范围检查，但我们仍然需要
 * 执行LIKE操作符以验证模式的其余部分。我们表示
 * 这样的索引条件是“损失型”的。当返回索引条件时，
 * 如果条件是损失型，您应将“损失型”请求字段
 * 设置为true；如果它是函数结果的精确等价物，
 * 则设置为false。核心代码会将该字段初始化为true，
 * 这是常见情况。
 *
 * 重要的是验证索引操作符家族是否是您要生成的
 * 条件的正确操作符家族。核心支持函数通常
 * 使用内置操作符族的已知OID，但扩展则需要
 * 更加努力，因为它们的OID不是固定的。对扩展
 * 数据类型的索引，一个可能可行的答案是验证
 * 索引AM的OID，然后假设您的数据类型只有一个
 * 相关的opclass，因此opfamily必须是正确的。
 * 生成OpExpr节点可能还需要知道扩展数据类型OID
 * （通常您可以通过对函数参数应用exprType()来找到）
 * 和操作符OID（您可以使用get_opfamily_member查找）。
 */
typedef struct SupportRequestIndexCondition
{
	NodeTag		type;

	/* 输入字段： */
	struct PlannerInfo *root;	/* 规划器的基础设施 */
	Oid			funcid;			/* 我们询问的函数 */
	Node	   *node;			/* 调用函数的解析节点 */
	int			indexarg;		/* 匹配索引列的函数参数索引 */
	struct IndexOptInfo *index; /* 规划器关于目标索引的信息 */
	int			indexcol;		/* 目标索引列的索引（0基） */
	Oid			opfamily;		/* 索引列的操作符家族 */
	Oid			indexcollation; /* 索引列的排序规则 */

	/* 输出字段： */
	bool		lossy;			/* 如果索引条件是函数调用的精确等价物则设置为false */
} SupportRequestIndexCondition;


/* ----------
 * 为支持任意单调递增和/或单调递减窗口函数的更高效查询执行，我们在规划器直接看到在子查询中引用窗口函数的 OpExpr 条件时，支持调用窗口函数的 prosupport 函数，并传递此结构体。 当规划器遇到这种情况时，我们填充此结构体并将其传递给窗口函数的 prosupport 函数，以便其可以评估给定的 WindowFunc 是否；
 *
 * a) 单调递增，或者
 * b) 单调递减，或者
 * c) 同时单调递增和递减，或者
 * d) 上述都不是。
 *
 * 单调递增的函数永远不会返回比“先前调用”中返回的值更低的值。 单调递减的函数永远不会返回比前一次调用返回的值更高的值。 同时具备这两者的函数每次必须返回相同的值。
 *
 * 我们定义“先前调用”为同一窗口分区中对相同 WindowFunc 结构的先前调用。
 *
 * row_number() 是单调递增函数的一个例子。 返回值将在每个新分区中重置为 1。 单调递增和递减函数的一个例子是 COUNT(*) OVER ()。 由于在此示例中没有 ORDER BY 子句，分区中的所有行都是同级的，分区内的所有行将位于框架边界内。 COUNT(*) OVER(ORDER BY a ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 同样也是如此。
 *
 * COUNT(*) OVER (ORDER BY a ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) 是单调递减函数的一个例子。
 *
 * 实现仅需关注给定 WindowFunc 在单个分区中的单调性。
 *
 * 输入：
 *	'window_func' 是指向正在调用的窗口函数的指针。
 *
 *	'window_clause' 指向 WindowClause 数据的指针。 支持函数可以利用此指针检查框架边界等。
 *
 * 输出：
 *	'monotonic' 是给定输入窗口函数和窗口子句的结果 MonotonicFunction 值。
 * ----------
 */
typedef struct SupportRequestWFuncMonotonic
{
	NodeTag		type;

	/* 输入字段： */
	WindowFunc *window_func;	/* 指向窗口函数数据的指针 */
	struct WindowClause *window_clause; /* 指向窗口子句数据的指针 */

	/* 输出字段： */
	MonotonicFunction monotonic;
} SupportRequestWFuncMonotonic;

#endif							/* SUPPORTNODES_H */
